import { send } from "./ws";
import { browser } from "webextension-polyfill-ts";
import CryptoJS from "crypto-js";
import { payURL, SubVersion, MainVersion, Platform, CodeTime } from "../config";
import {
    getvpnStore,
    setvpnStore,
    setConnectIcon,
    gotoPage,
    errorLog,
    isChrome,
} from "../utils";

type ISendEmail = {
    Email: string;
    Platform: "Chrome" | "FireFox" | "Edge";
};
type ILogin = {
    Email: string;
    VerifyCode: string;
    Session: string;
    OsType: string;
    Platform: "Chrome" | "FireFox" | "Edge";
    MainVersion: number;
    SubVersion: number;
};

export const storage = {
    get: (key: string) => {
        return browser.storage.local.get(key);
    },
    set: (items: any) => {
        return browser.storage.local.set(items);
    },
    clear: () => {
        return browser.storage.local.clear();
    },
};

export async function sendEmail(data: ISendEmail) {
    try {
        const result: any = await send({ Cmd: 7, Body: data });
        if (result.Code === 0) {
            startCountDown();
        }
        return result;
    } catch (e) {
        return errorLog(e);
    }
}

export async function login(data: ILogin) {
    try {
        const result: any = await send({ Cmd: 8, Body: data });
        if (result.Code === 0) {
            await setvpnStore({
                isLogin: true,
                Email: data.Email,
                vip: result.Body.Vip,
                userInfo: result.Body,
            });
            restCountDown();
        } else {
            return Promise.reject("登录失败");
        }
        return result;
    } catch (e) {
        return errorLog(e);
    }
}
export async function logout() {
    localStorage.Email = "";
    return logoutSource();
}

export async function disConnectVPN() {
    try {
        const result: any = await send({ Cmd: 35, Body: null });
        return result;
    } catch (e) {
        return errorLog(e);
    }
}

export async function loginConnect({ loginData, currentLine }: any) {
    try {
        await disConnectVPN();
        const result = await login(loginData);
        await createProxy(currentLine);
        return result;
    } catch (e) {
        return Promise.reject(e);
    }
}

export async function getNodes() {
    try {
        const result: any = await send({ Cmd: 11, Body: null });
        await setNodeList(result);
        return result;
    } catch (e) {
        return errorLog(e);
    }
}

export async function createProxy(currentLine: any) {
    try {
        const connected = await createProxyauth(currentLine);
        await setvpnStore({ connected });
        return connected;
    } catch (e) {
        return errorLog(e);
    }
}

export async function closeProxy(isSend = true) {
    try {
        await setvpnStore({ connected: false });
        return closeProxyauth(isSend);
    } catch (e) {
        return errorLog(e);
    }
}

export async function syncNodesCreateProxy(currentLine: any) {
    try {
        const NodeList: any = await getNodes();
        if (NodeList.Body.NodeList) {
            const nodeList = lineNodes(NodeList.Body.NodeList);
            const node = nodeList.find(
                (node: any) => node.Id === currentLine.Id
            );
            if (node) {
                await setvpnStore({
                    currentLine: node,
                });
                return checkProxyCreate(node);
            } else {
                return Promise.reject("节点列表中未找到当前节点");
            }
        } else {
            return Promise.reject("获取到节点列表为空列表");
        }
    } catch (e) {
        return errorLog(e);
    }
}

export async function localNodesCreateProxy(currentLine: any) {
    try {
        const { lineData } = await getvpnStore();
        const node = findLineDataNode(lineData, currentLine);
        if (node) {
            await setvpnStore({
                currentLine: node,
            });
            return checkProxyCreate(node);
        } else {
            return Promise.reject("节点列表中未找到当前节点");
        }
    } catch (e) {
        return errorLog(e);
    }
}

export async function checkProxyCreate(currentLine: any) {
    try {
        const vpnStore = await getvpnStore();
        const { isLogin, vip } = vpnStore;

        if (checkProxyVIP(currentLine, isLogin, vip)) {
            return true;
        } else {
            const connected = await createProxy(currentLine);
            if (connected) {
                return false;
            } else {
                return Promise.reject("此节点的负载已满，请更换节点");
            }
        }
    } catch (e) {
        return errorLog(e);
    }
}

//product
export async function getShops() {
    try {
        const result: any = await send({ Cmd: 13, Body: null });
        return setvpnStore({
            shopList: result.Body.ShopList || [],
            shopChanged: false,
        });
    } catch (e) {
        return errorLog(e);
    }
}

export async function buy(data: any) {
    try {
        const result: any = await send({ Cmd: 21, Body: data });
        if (result.Code === 0) {
            let ciphertext = CryptoJS.AES.encrypt(
                JSON.stringify(result.Body),
                "PNgCykN2NyueLVkQ"
            ).toString();
            let cipherData = CryptoJS.enc.Base64.stringify(
                CryptoJS.enc.Utf8.parse(ciphertext)
            );
            gotoPage(`${payURL}/#/pay?order=${cipherData}`);
            return result;
        } else {
            return Promise.reject(result.Body.ErrMsg);
        }
    } catch (e) {
        return errorLog(e);
    }
}

localStorage.Countdown = 0;
let count = 0;
let countTimer: any = null;
export function startCountDown() {
    count = CodeTime;
    countTimer = setInterval(() => {
        if (count > 0) {
            count -= 1;
            localStorage.Countdown = count;
            browser.storage.local.set({ Countdown: count });
        } else {
            clearInterval(countTimer);
        }
    }, 1000);
}

export function restCountDown() {
    clearInterval(countTimer);
    localStorage.Countdown = 0;
    count = 0;
    browser.storage.local.set({ Countdown: count });
}

//proxy code
export function random(num: number) {
    return Math.floor(Math.random() * num);
}

export function notifications(result: string, opt?: any) {
    browser.notifications.create("vpn-notifications", {
        type: "basic",
        iconUrl: browser.runtime.getURL("assets/icons/favicon-128.png"),
        title: "菠萝VPN",
        message: result,
        ...opt,
    });
}

export function tabsReload() {
    browser.tabs.query({ currentWindow: true }).then((tabs) => {
        tabs.forEach((tab) => {
            browser.tabs.reload(
                tab.id, // optional integer
                { bypassCache: true } // optional object
            );
        });
    });
}

export function firefoxPac(pacScript: string) {
    return URL.createObjectURL(
        new Blob([pacScript], { type: "text/javascript" })
    );
}

export async function queryLine(addr: string) {
    try {
        return await send({ Cmd: 33, Body: { ServerAddr: addr } });
    } catch (e) {
        return errorLog(e);
    }
}

let cacheLine: any = [];

export async function createProxyauth(line: any): Promise<any> {
    try {
        let Addr = line.Addr[random(line.Addr.length)];
        const Addrs = line.Addr.filter(
            (line: string) => !cacheLine.includes(line)
        );
        if (Addrs.length) {
            Addr = Addrs[random(Addrs.length)];
        }
        const result: any = await queryLine(Addr);
        if (result.Body.Success) {
            cacheLine = [];
            await createProxySource(Addr, line);
            return true;
        } else {
            return resetProxy(Addr, line);
        }
    } catch (e) {
        return errorLog(e);
    }
}

export async function resetProxy(Addr: string, line: any) {
    try {
        cacheLine = Array.from(new Set([...cacheLine, Addr]));
        if (cacheLine.length === line.Addr.length) {
            notifications("此节点的负载已满，请更换节点");
            cacheLine = [];
            return false;
        } else {
            return createProxyauth(line);
        }
    } catch (e) {
        return errorLog(e);
    }
}

export async function createProxySource(Addr: string, line: any) {
    try {
        let addr = Addr.split(":");
        let pacScript = `var FindProxyForURL = function(init, profiles) {
            return function(url, host) {
                "use strict";
                var result = init, scheme = url.substr(0, url.indexOf(":"));
                do {
                    result = profiles[result];
                    if (typeof result === "function") result = result(url, host, scheme);
                } while (typeof result !== "string" || result.charCodeAt(0) === 43);
                return result;
            };
        }("+proxy", {
            "+proxy": function(url, host, scheme) {
                "use strict";
                if (/^127\.0\.0\.1$/.test(host) || /^::1$/.test(host) || /^localhost$/.test(host) || /^192\.168\./.test(host) || /^10\./.test(host) || /^172\.16\./.test(host) || /^172\.31\./.test(host)) return "DIRECT";
                return "HTTPS ${addr[0]}:${addr[1]}";
            }
        });`;
        if (isChrome()) {
            const config = {
                mode: "pac_script",
                pacScript: {
                    data: pacScript,
                },
            };
            // eslint-disable-next-line @typescript-eslint/no-empty-function

            browser.proxy.settings.set(
                { value: config, scope: "regular" },
                function () {
                    tabsReload();
                }
            );

            // eslint-disable-next-line @typescript-eslint/no-unused-vars
        } else {
            let proxySettings = {
                proxyType: "autoConfig",
                autoConfigUrl: firefoxPac(pacScript),
            };

            browser.proxy.settings.set({ value: proxySettings }).then(() => {
                tabsReload();
            });
        }

        setConnectIcon(true);
        return setvpnStore({
            currentLine: {
                ...line,
                Proxy: Addr,
            },
        });
    } catch (e) {
        return errorLog(e);
    }
}

export async function closeProxyauth(isSend = true) {
    try {
        if (isChrome()) {
            browser.proxy.settings.set(
                {
                    value: { mode: "system" },
                    scope: "regular",
                },
                () => {
                    tabsReload();
                }
            );
        } else {
            browser.proxy.settings
                .set({ value: { proxyType: "system" } })
                .then(() => {
                    tabsReload();
                });
        }
        setConnectIcon(false);
        if (isSend) {
            return send({ Cmd: 35, Body: null });
        } else {
            return true;
        }
    } catch (e) {
        return errorLog(e);
    }
}

export async function restProxyStauts() {
    try {
        const vpnStore = await getvpnStore();
        const { currentLine } = vpnStore;
        if (currentLine && currentLine?.Addr) {
            cacheLine = Array.from(new Set([...cacheLine, currentLine.Proxy]));
            if (cacheLine.length === currentLine.Addr.length) {
                cacheLine = [];
                return false;
            } else {
                await createProxyauth(currentLine);
                return true;
            }
        } else {
            return false;
        }
    } catch (e) {
        return errorLog(e);
    }
}

export async function logoutSource() {
    try {
        const vpnStore = await getvpnStore();
        const { currentLine, connected } = vpnStore;
        if (connected) {
            if (currentLine.Vip) {
                await closeProxy();
                return sendLogout({ ...vpnStore, connected: false });
            } else {
                await send({ Cmd: 35, Body: null });
                await sendLogout(vpnStore);
                const connected = await createProxyauth(currentLine);
                return storage.set({
                    vpnStore: {
                        ...vpnStore,
                        connected,
                        isLogin: false,
                        vip: false,
                    },
                });
            }
        } else {
            return sendLogout(vpnStore);
        }
    } catch (e) {
        return errorLog(e);
    }
}

export async function sendLogout(vpnStore: any) {
    try {
        await send({
            Cmd: 8,
            Body: {
                ClientIdent: localStorage.ClientIdent,
                Email: "",
                VerifyCode: "",
                Session: "",
                OsType: "IE",
                Platform: Platform(),
                MainVersion: MainVersion,
                SubVersion: SubVersion,
            },
        });
        return storage.set({
            vpnStore: { ...vpnStore, isLogin: false, vip: false },
        });
    } catch (e) {
        return errorLog(e);
    }
}

export async function proxyStatus(): Promise<boolean> {
    return new Promise((resovle) => {
        if (isChrome()) {
            browser.proxy.settings.get(
                { incognito: false },
                function (config: any) {
                    if (
                        config["value"]["mode"] == "system" ||
                        config["value"]["mode"] == "direct"
                    ) {
                        setConnectIcon(false);
                        resovle(false);
                    } else {
                        setConnectIcon(true);
                        resovle(true);
                    }
                }
            );
        } else {
            browser.proxy.settings.get({ incognito: false }).then((config) => {
                if (
                    config["value"]["mode"] == "system" ||
                    config["value"]["mode"] == "direct"
                ) {
                    setConnectIcon(false);
                    resovle(false);
                } else {
                    setConnectIcon(true);
                    resovle(true);
                }
            });
        }
    });
}

export function payNotice(data: any) {
    browser.tabs.query({ url: `${payURL}/*` }).then((tabs) => {
        const payTabs = tabs.filter(
            (tab: any) => String(tab.url).indexOf("/#/pay?") > -1
        );
        payTabs.forEach((tab: any) => {
            browser.tabs.sendMessage(tab.id, { type: "payStatus", data: data });
        });
    });
}

export async function closeProxyStauts(str: string) {
    notifications(str);
    browser.browserAction.setIcon({
        path: "assets/icons/off.png",
    });

    return await closeProxy();
}

//凡是调用lineGroup 都更新一下最新的currentLine数据
export async function updateCurrentLine(nodeList: any[]) {
    const vpnStore = await getvpnStore();

    const { currentLine } = vpnStore;
    if (currentLine && currentLine.NodeName) {
        const node = lineNodes(nodeList).find(
            (node: any) => node.Id === currentLine.Id
        );
        if (node) {
            return setvpnStore({
                lineData: lineGroup(nodeList),
                currentLine: node,
                nodesChanged: false,
            });
        } else {
            return setvpnStore({
                lineData: lineGroup(nodeList),
                nodesChanged: false,
            });
        }
    } else {
        return setvpnStore({
            lineData: lineGroup(nodeList),
            nodesChanged: false,
        });
    }
}

export async function setNodeList(NodeList: any) {
    if (NodeList.Body.NodeList) {
        return updateCurrentLine(NodeList.Body.NodeList);
    } else {
        const vpnStore = await getvpnStore();
        if (vpnStore.connected) {
            await closeProxy();
            return setvpnStore({
                lineData: [],
                nodesChanged: false,
                connected: false,
            });
        } else {
            return setvpnStore({
                lineData: [],
                nodesChanged: false,
            });
        }
    }
}

export function checkProxyVIP(
    currentLine: any,
    isLogin: boolean,
    vip: boolean
) {
    if (currentLine.Vip) {
        if (isLogin && vip) {
            return false;
        } else {
            return true;
        }
    } else {
        return false;
    }
}

export function findLineDataNode(lineData: any[], currentLine: any) {
    let nodeList = lineData.flatMap((line: any) => line.children);
    const node = nodeList.find((node) => node.Id === currentLine.Id);
    return node;
}

export function lineNodes(data: any[]) {
    let nodeMap: any = {};
    data.forEach((line) => {
        let key = line.NodeName + line.LineName;
        if (nodeMap[key]) {
            nodeMap[key].Addr.push(line.Addr);
        } else {
            nodeMap[key] = {
                Id: key,
                Icon: line.Icon,
                NodeName: line.NodeName,
                LineName: line.LineName,
                Addr: [line.Addr],
                Proxy: "",
                Vip: line.Vip,
                VPNType: line.VPNType,
            };
        }
    });
    return Object.values(nodeMap);
}

export function lineGroup(data: any[]) {
    let nodeMap: any = {};
    let lineMap: any = {};
    data.forEach((line) => {
        let key = line.NodeName + line.LineName;
        if (nodeMap[key]) {
            nodeMap[key].Addr.push(line.Addr);
        } else {
            nodeMap[key] = {
                Id: key,
                Icon: line.Icon,
                NodeName: line.NodeName,
                LineName: line.LineName,
                Addr: [line.Addr],
                Proxy: "",
                Vip: line.Vip,
                VPNType: line.VPNType,
            };
        }
    });
    const nodeList = Object.values(nodeMap);
    //updateCurrentLine(nodeList);
    nodeList.forEach((node: any) => {
        if (lineMap[node.NodeName]) {
            lineMap[node.NodeName].children.push(node);
        } else {
            lineMap[node.NodeName] = {
                Icon: node.Icon,
                NodeName: node.NodeName,
                children: [node],
            };
        }
    });

    return Object.values(lineMap);
}

export function checkCreateProxy(
    currentNode: any,
    vip: boolean,
    isLogin: boolean,
    connected: boolean
) {
    if (currentNode.NodeName) {
        if (currentNode.Vip) {
            if (vip && isLogin && connected) {
                return true;
            } else {
                return false;
            }
        } else {
            if (connected) {
                return true;
            } else {
                return false;
            }
        }
    } else {
        return false;
    }
}
//更新通知
export function updateNotifications(result: string, updateURL: string) {
    if (isChrome()) {
        updateChromeNotifications(result, updateURL);
    } else {
        updateFirefoxNotifications(result, updateURL);
    }
}

export function updateChromeNotifications(result: string, updateURL: string) {
    browser.notifications.create("vpn-update", {
        type: "basic",
        iconUrl: browser.runtime.getURL("assets/icons/favicon-128.png"),
        title: "菠萝VPN",
        message: result,
        buttons: [{ title: "更新" }, { title: "取消" }],
    });

    function buttonCallback(id: string, index: number) {
        if (id === "vpn-update") {
            if (index === 0) {
                gotoPage(updateURL);
            }
            browser.notifications.onButtonClicked.removeListener(
                buttonCallback
            );
        }
    }
    browser.notifications.onButtonClicked.addListener(buttonCallback);
}

export function updateFirefoxNotifications(result: string, updateURL: string) {
    browser.notifications.create("vpn-update", {
        type: "basic",
        iconUrl: browser.runtime.getURL("assets/icons/favicon-128.png"),
        title: "菠萝VPN",
        message: result,
    });

    function callback(id: string) {
        if (id === "vpn-update") {
            gotoPage(updateURL);
            browser.notifications.onClicked.removeListener(callback);
        }
    }
    browser.notifications.onClicked.addListener(callback);
}

export {
    getvpnStore,
    setvpnStore,
    setConnectIcon,
    gotoPage,
    errorLog,
    isChrome,
};
